package com.foreverwin.jzymes.archive.service.impl;

import com.foreverwin.jzymes.archive.domain.DataArchive;
import com.foreverwin.jzymes.archive.mapper.DataArchiveAdviceMapper;
import com.foreverwin.jzymes.archive.mapper.DataArchiveMapper;
import com.foreverwin.jzymes.archive.service.IDataArchiveAdviceService;
import com.foreverwin.jzymes.framework.annotation.DataSource;
import com.foreverwin.jzymes.framework.datasource.DataSourceType;
import com.foreverwin.jzymes.framework.util.I18nUtil;
import com.foreverwin.jzymes.framework.util.SpringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.type.JdbcType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class DataArchiveAdviceServiceImpl implements IDataArchiveAdviceService {

    @Autowired
    DataArchiveAdviceMapper dataArchiveAdviceMapper;
    @Autowired
    DataArchiveMapper dataArchiveMapper;

    public List<String> findTableFields(String table ){
        return dataArchiveAdviceMapper.selectTableColumns( table );
    }

    @DataSource( value = DataSourceType.SLAVE )
    public List<String> findArchiveTableFields( String table ){
        return dataArchiveAdviceMapper.selectArchiveTableColumns( table );
    }

    public List<DataArchive> findReferDataArchiveList( String table ){
        return dataArchiveAdviceMapper.selectDataArchiveList( table );
    }

    @DataSource( value = DataSourceType.SLAVE )
    public int createArchiveTable( String table, String archiveTable ){
        return dataArchiveAdviceMapper.createArchiveTable( table, archiveTable );
    }

    public int updateArchiveCount( String table, int increment ){
        return dataArchiveAdviceMapper.updateArchiveCount( table, increment );
    }

    public List<Map<String,Object>> doQuery(Map<String,Object> paramMap ){
        return dataArchiveAdviceMapper.buildQuerySql( paramMap );
    }

    @DataSource( value = DataSourceType.SLAVE )
    public int doBatchInsertArchive( List<Map<String, Object>> dataList, String archiveTable, List<String> syncFields ){
        SqlSessionFactory sqlSessionFactory = SpringUtils.getBean( SqlSessionFactory.class );
        SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, false);
        sqlSession.getConfiguration().setJdbcTypeForNull( JdbcType.NULL );
        try{
            DataArchiveAdviceMapper dataArchiveAdviceMapper = sqlSession.getMapper( DataArchiveAdviceMapper.class );
            for( Map<String,Object> data : dataList ){
                Map<String, Object> paramMap = new HashMap<>();
                paramMap.put("table", archiveTable );
                List fields = new ArrayList();
                List values = new ArrayList();

                for( String key : syncFields ){
                    fields.add( key );
                    values.add( data.get( key ) );
                }
                paramMap.put("fields", String.join( ",", fields ) );
                paramMap.put("values", values );

                dataArchiveAdviceMapper.buildInsertSql( paramMap );
            }
            sqlSession.commit();
        }finally {
            sqlSession.close();
        }
        return dataList.size();
    }

    @DataSource( value = DataSourceType.SLAVE )
    public void doBatchDeleteArchive( List<Map<String, Object>> dataList, String archiveTable, List<String> primaryKeys ){
        SqlSessionFactory sqlSessionFactory = SpringUtils.getBean( SqlSessionFactory.class );
        SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, false);
        try{
            DataArchiveAdviceMapper dataArchiveAdviceMapper = sqlSession.getMapper( DataArchiveAdviceMapper.class );
            for( Map<String,Object> data : dataList ){
                Map<String, Object> paramMap = new HashMap<>();
                paramMap.put("table", archiveTable );

                Map criterias = new HashMap();
                for( String field : primaryKeys ){
                    criterias.put( field, data.get( field ) );
                }
                paramMap.put("criterias", criterias );

                dataArchiveAdviceMapper.buildDeleteSql(paramMap);
            }
            sqlSession.commit();
        }finally {
            sqlSession.close();
        }
    }

    public void doBatchDelete( List<Map<String, Object>> dataList, String table, List<String> primaryKeys ){
        SqlSessionFactory sqlSessionFactory = SpringUtils.getBean( SqlSessionFactory.class );
        SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, false);
        try{
            DataArchiveAdviceMapper dataArchiveAdviceMapper = sqlSession.getMapper( DataArchiveAdviceMapper.class );
            for( Map<String,Object> data : dataList ){
                Map<String, Object> paramMap = new HashMap<>();
                paramMap.put("table", table );

                Map criterias = new HashMap();
                for( String field : primaryKeys ){
                    criterias.put( field, data.get( field ) );
                }
                paramMap.put("criterias", criterias );

                dataArchiveAdviceMapper.buildDeleteSql(paramMap);
            }
            sqlSession.commit();
        }finally {
            sqlSession.close();
        }
    }

    @DataSource( value = DataSourceType.SLAVE )
    public int doDeleteArchive( Map data, String archiveTable, String queryCriteria ){
        Map<String,Object> paramMap = new HashMap<>( data );
        paramMap.put( "archiveTable", archiveTable );
        paramMap.put( "queryCriteria", queryCriteria );
        return dataArchiveAdviceMapper.buildDeleteArchiveSql( paramMap );
    }

    //构建ODS插入sql
    @DataSource( value = DataSourceType.SLAVE )
    public int doInsertArchive(  Map<String,Object> data, String archiveTable, List<String> syncFields ){
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("table", archiveTable );
        List fields = new ArrayList();
        List values = new ArrayList();

        for( String key : data.keySet() ){
            if( syncFields.contains( key ) && data.get( key ) != null ){
                fields.add( key );
                values.add( data.get( key ) );
            }
        }
        paramMap.put("fields", String.join( ",", fields ) );
        paramMap.put("values", values );

        return dataArchiveAdviceMapper.buildInsertSql(paramMap);
    }

    //构建ODS更新sql
    @DataSource( value = DataSourceType.SLAVE )
    public int doUpdateArchive( Map<String,Object> data, String archiveTable, List<String> syncFields, List<String> primaryKeys ){
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("table", archiveTable );

        List nullFields = new ArrayList();
        Map values = new HashMap();
        Map criterias = new HashMap();
        for( String key : data.keySet() ){
            if( syncFields.contains( key ) ){
                if( data.get( key ) != null ){
                    values.put( key, data.get( key ) );
                }else{
                    nullFields.add( key + "=null" );
                }
            }
            if( primaryKeys.contains( key ) ){
                criterias.put( key, data.get( key ) );
            }
        }
        paramMap.put("nullFields", nullFields );
        paramMap.put("values", values );
        paramMap.put("criterias", criterias );

        return dataArchiveAdviceMapper.buildUpdateSql(paramMap);
    }

    //构建WIP删除sql
    public int doDelete( Map data, String table, List<String> primaryKeys ){
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("table", table );

        Map criterias = new HashMap();
        for( String field : primaryKeys ){
            criterias.put( field, data.get( field ) );
        }
        paramMap.put("criterias", criterias );

        return dataArchiveAdviceMapper.buildDeleteSql(paramMap);
    }

    @DataSource( value = DataSourceType.SLAVE )
    public int doInsert( Map<String, Object> paramMap ){
        return dataArchiveAdviceMapper.buildInsertSql( paramMap );
    }

    //构建ODS更新sql
    @DataSource( value = DataSourceType.SLAVE )
    public int doUpdate( Map<String, Object> paramMap ){
        return dataArchiveAdviceMapper.buildUpdateSql(paramMap);
    }

    //构建WIP删除sql
    public int doDelete( Map<String, Object> paramMap ){
        return dataArchiveAdviceMapper.buildDeleteSql(paramMap);
    }



    //@Transactional( propagation = Propagation.REQUIRED, rollbackFor = Exception.class )
    public void saveDataArchive( List<DataArchive> dataArchiveList ){
        List<DataArchive> oldDataArchiveList = dataArchiveMapper.selectDataArchiveList( null );
        for( DataArchive dataArchive : oldDataArchiveList ){
            boolean exists = false;
            for( DataArchive dataArchive1 : dataArchiveList ){
                if( dataArchive.getTableName().equals( dataArchive1.getTableName() ) ){
                    exists = true;
                    break;
                }
            }
            if( !exists ){
                dataArchiveMapper.deleteDataArchiveById( dataArchive.getTableName() );
            }
        }
        for( DataArchive dataArchive : dataArchiveList ){
            checkDataArchive( dataArchive, dataArchiveList );
            if( dataArchiveMapper.updateDataArchive( dataArchive ) == 0 ){
                dataArchiveMapper.insertDataArchive( dataArchive );
            }
        }
    }

    public void checkDataArchive( DataArchive dataArchive, List<DataArchive> dataArchiveList ){
        Assert.hasText( dataArchive.getTableName(), I18nUtil.getI18nText( "archive.tableNameNotBlank" ) );
        Assert.hasText( dataArchive.getArchiveTable(), I18nUtil.getI18nText( "archive.archiveTableNotBlank", new String[]{ dataArchive.getTableName() } ) );
        Assert.hasText( dataArchive.getPrimaryKey(), I18nUtil.getI18nText( "archive.primaryKeyNotBlank", new String[]{ dataArchive.getTableName() } ) );

        /**检查归档表在数据库是否存在*/
        String table = dataArchive.getTableName();
        List<String> tableFields = findTableFields( table );
        Assert.notEmpty( tableFields, I18nUtil.getI18nText( "archive.tableNotExist", new String[]{ table } ) );

        /**检查归档目标表在数据库是否存在*/
        IDataArchiveAdviceService dataArchiveAdviceService = SpringUtils.getBean(IDataArchiveAdviceService.class);
        String archiveTable = dataArchive.getArchiveTable();
        List<String> archiveTableFields = dataArchiveAdviceService.findArchiveTableFields( archiveTable );
        if( archiveTableFields.size() == 0 ){
            dataArchiveAdviceService.createArchiveTable( dataArchive.getTableName(), dataArchive.getArchiveTable() );
        }

        /**检查引用归档配置是否存在*/
        String referTable = dataArchive.getReferTable();
        if( StringUtils.isNotBlank( referTable ) ){
            boolean exists = false;
            for( DataArchive dataArchive1 : dataArchiveList ){
                if( dataArchive1.getTableName().equals( referTable ) ){
                    exists = true;
                    break;
                }
            }
            Assert.isTrue( exists, I18nUtil.getI18nText( "archive.referDataArchiveNotExist", new String[]{ dataArchive.getTableName(), referTable }  ) );
        }
    }

}
